/* 
 * File:   RegularGridIterator.cpp
 * Author: walther
 * 
 * Created on October 20, 2012, 9:25 PM
 */

#include <float.h>

#include "RegularGridIterator.h"

RegularGridIterator::RegularGridIterator( RegularGrid* grid, Ray& ray ) : GridIterator( ray )
{
    _grid = grid;
    
    double t;
    Box* bbox = _grid->getBoundingBox();
    
    Vector4D boxMin = bbox->getMin();
    Vector4D boxMax = bbox->getMax();
    Vector4D origin = _ray->origin;
    
    // Tests if the origin of the ray is inside the bounding box
    if (origin.x <= boxMax.x && origin.x >= boxMin.x &&
        origin.y <= boxMax.y && origin.y >= boxMin.y &&
        origin.z <= boxMax.z && origin.z >= boxMin.z)
    {
        _finished = false;
        
        _grid->getCellIndex( origin, _currentCellX, _currentCellY, _currentCellZ );
        
        int nx, ny, nz;
        _grid->getCellNumber( nx, ny, nz );
        if (_currentCellX >= nx) _currentCellX = nx - 1;
        if (_currentCellY >= ny) _currentCellY = ny - 1;
        if (_currentCellZ >= nz) _currentCellZ = nz - 1;
        
        computeTraversalParameters();
    }
    // Gets the intersection point on the edge of the bounding box
    else if (bbox->computeRayIntersection( *_ray, t ))
    {
        _finished = false;
        
        // Computes the intersection point
        Vector4D intersection = _ray->origin + t*_ray->direction;
        _grid->getCellIndex( intersection, _currentCellX, _currentCellY, _currentCellZ );
        
        int nx, ny, nz;
        _grid->getCellNumber( nx, ny, nz );
        if (_currentCellX >= nx) _currentCellX = nx - 1;
        if (_currentCellY >= ny) _currentCellY = ny - 1;
        if (_currentCellZ >= nz) _currentCellZ = nz - 1;
        
        
        // Initializes the variables
        computeTraversalParameters();
    }
    // No intersection at all
    else
    {
        _finished = true;
    }    
}



RegularGridIterator::~RegularGridIterator()
{
    //TODO
}



GridCell* RegularGridIterator::next()
{
    if (_finished) return 0;
    
    GridCell* cell = _grid->getCell( _currentCellX, _currentCellY, _currentCellZ );
    
    if (_nextX < _nextY)
    {
        if (_nextX < _nextZ)
        {
            _currentCellX += _stepX;
            _nextX += _deltaX;
        }
        else //_nextX > _nextZ
        {
            _currentCellZ += _stepZ;
            _nextZ += _deltaZ;
        }
    }
    else //_nextX > _nextY
    {
        if (_nextY < _nextZ)
        {
            _currentCellY += _stepY;
            _nextY += _deltaY;
        }
        else //_nextY > _nextZ
        {
            _currentCellZ += _stepZ;
            _nextZ += _deltaZ;
        }
    }
    
    int nx, ny, nz;
    _grid->getCellNumber( nx, ny, nz );
    
    if (_currentCellX >= nx || _currentCellX < 0 ||
        _currentCellY >= ny || _currentCellY < 0 ||
        _currentCellZ >= nz || _currentCellZ < 0   )
    {
        _finished = true;
    }
    
    return cell;
}



void RegularGridIterator::computeTraversalParameters()
{
    Box* bbox = _grid->getBoundingBox();
    Vector4D cellSizes = _grid->getCellSizes();
    
    // Computes the step in each direction    
    _stepX = 0;
    _stepY = 0;
    _stepZ = 0;

    // StepX
    if (_ray->direction.x > 0.0)
        _stepX = 1;
    else if (_ray->direction.x < 0.0)
        _stepX = -1;
        
    // StepY
    if (_ray->direction.y > 0.0)
        _stepY = 1;
    else if (_ray->direction.y < 0.0)
        _stepY = -1;
    
    // StepZ
    if (_ray->direction.z > 0.0)
        _stepZ = 1;
    else if (_ray->direction.z < 0.0)
        _stepZ = -1;
    
    if (_ray->direction.x != 0)
    {
        double nextXEdge;
        double currentXEdge;
        
        if (_stepX > 0)
        {
            currentXEdge = bbox->getMin().x + _currentCellX*cellSizes.x;
            nextXEdge    = bbox->getMin().x + (_currentCellX + 1)*cellSizes.x;
        }
        else
        {
            currentXEdge = bbox->getMin().x + (_currentCellX+1)*cellSizes.x;
            nextXEdge    = bbox->getMin().x + _currentCellX*cellSizes.x;
        }
        
        double currentX = (currentXEdge - _ray->origin.x)/_ray->direction.x;
        _nextX = (nextXEdge - _ray->origin.x)/_ray->direction.x;
        _deltaX = _nextX - currentX;
    }
    else
    {
        _nextX = DBL_MAX;
        _deltaX = 0.0;
    }

    if (_ray->direction.y != 0)
    {
        double nextYEdge; //= bbox->getMin().y + (_currentCellY + _stepY)*cellSizes.y;
        double currentYEdge; //= bbox->getMin().y + _currentCellY*cellSizes.y;
        
        if (_stepY > 0)
        {
            currentYEdge = bbox->getMin().y + _currentCellY*cellSizes.y;
            nextYEdge    = bbox->getMin().y + (_currentCellY + 1)*cellSizes.y;
        }
        else
        {
            currentYEdge = bbox->getMin().y + (_currentCellY+1)*cellSizes.y;
            nextYEdge    = bbox->getMin().y + _currentCellY*cellSizes.y;
        }
        
        double currentY = (currentYEdge - _ray->origin.y)/_ray->direction.y;
        _nextY = (nextYEdge - _ray->origin.y)/_ray->direction.y;
        _deltaY = _nextY - currentY;
    }
    else
    {
        _nextY = DBL_MAX;
        _deltaY = 0.0;
    }

    if (_ray->direction.z != 0)
    {
        double nextZEdge; //= bbox->getMin().z + (_currentCellZ + _stepZ)*cellSizes.z;
        double currentZEdge; //= bbox->getMin().z + _currentCellZ*cellSizes.z;
        
        if (_stepZ > 0)
        {
            currentZEdge = bbox->getMin().z + _currentCellZ*cellSizes.z;
            nextZEdge    = bbox->getMin().z + (_currentCellZ + 1)*cellSizes.z;
        }
        else
        {
            currentZEdge = bbox->getMin().z + (_currentCellZ+1)*cellSizes.z;
            nextZEdge    = bbox->getMin().z + _currentCellZ*cellSizes.z;
        }
        
        double currentZ = (currentZEdge - _ray->origin.z)/_ray->direction.z;
        _nextZ = (nextZEdge - _ray->origin.z)/_ray->direction.z;
        _deltaZ = _nextZ - currentZ;
    }
    else
    {
        _nextZ = DBL_MAX;
        _deltaZ = 0.0;            
    }
}
